using System;
using System.Collections.Generic;
using NaughtyAttributes;
using SimpleJSON;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Gj
{
    public class ListHelper : NodeHelper, IEndDragHandler
    {
        public GameObject item;
        public GameObject content;
        public GameObject empty;
        public GameObject detail;
        [SelectRef(UIKey.DATA_KEY)] public SelectKey selectIndexKey;
        [SelectRef(UIKey.DATA_KEY)] public SelectKey filterKey;
        public bool refresh;
        public bool vertical;
        public bool horizontal;
        public bool isCenter;
        public bool isFixed;
        public bool isFill;
        public bool isFull;
        public bool isGrid;
        public bool isAuto;
        public bool isSelect;
        public bool isHide;
        public bool isChange;
        public bool isMultiple;
        public bool isCache;
        public bool isKey;
        public int number;
        public int maxNumber;
        public int minNumber;
        public int total;
        public Vector2 selectSize;
        public AdaptiveSize adaptiveSize;
        public RectTransform rectTransform;
        public RectTransform parentRectTransform;
        public LayoutGroup layoutGroup;
        private Vector2 _cellSize;
        private float _currentHeight;
        private float _currentWidth;
        private float _height;
        private ScrollRect _ScrollRect;
        private float _width;
        private int _lastTotal;

        private readonly List<GameObject> list = new();
        private int selectIndex = -1;

        protected override BindDataType bindDataType => BindDataType.List;

        private ScrollRect ScrollRect
        {
            get
            {
                if (_ScrollRect == null) _ScrollRect = GetComponent<ScrollRect>();
                if (_ScrollRect == null) _ScrollRect = transform.parent.GetComponent<ScrollRect>();
                return _ScrollRect;
            }
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            if (refresh)
            {
                float v = 0;
                if (vertical) v = ScrollRect.verticalNormalizedPosition;
                if (horizontal) v = ScrollRect.horizontalNormalizedPosition;
                if (v < 0) Emit(UIEvent.PullUp);
                if (v > 1) Emit(UIEvent.PullDown);
            }
        }

        protected override void Init()
        {
            base.Init();
            if (content == null) content = gameObject;
            if (rectTransform == null) rectTransform = content.GetComponent<RectTransform>();
            if (isGrid && _width == 0) _cellSize = content.GetComponent<GridLayoutGroup>().cellSize;
            if (_height == 0 && parentRectTransform != null) _height = parentRectTransform.rect.height;
            if (_width == 0 && parentRectTransform != null) _width = parentRectTransform.rect.width;
        }

        public override void SetData(JSONObject json)
        {
            if (!selectIndexKey.IsEmpty()) selectIndex = selectIndexKey.Get(json);
            if (debug) Debug.LogErrorFormat("{0} {1}", selectIndexKey, selectIndex);
            base.SetData(json);
        }

        protected override void BindData(JSONArray array)
        {
            var all = 0;
            total = 0;
            for (var i = 0; i < array.Count; i++)
            {
                var obj = array[i] == null ? new JSONObject() : array[i].AsObject;
                if (list.Count <= all) list.Add(Instantiate(item, content.transform));
                var o = list[all];
                if (filterKey.IsEmpty())
                {
                    o.SetActive(true);
                    total++;
                }
                else
                {
                    if (filterKey.Get(obj).AsBool)
                    {
                        o.SetActive(true);
                        total++;
                    }
                    else
                    {
                        o.SetActive(false);
                    }
                }

                ItemHandle(all, o, obj);
                all++;
            }

            if (total == 0)
            {
                if (empty != null) empty.SetActive(true);
                // gameObject.SetActive(false);
                if (detail != null) detail.SetActive(false);
            }
            else
            {
                if (empty != null) empty.SetActive(false);
                // gameObject.SetActive(true);
                if (detail != null) detail.SetActive(true);
            }

            for (var i = all; i < list.Count; i++) list[i].SetActive(false);

            if (isHide && total == 0)
                content.SetActive(false);
            else
                content.SetActive(true);

            if (isCache)
            {
                if (_lastTotal != total) Refresh();
                _lastTotal = total;
            }
            else
            {
                Refresh();
            }
        }

        protected void GridCompute()
        {
            var grid = content.GetComponent<GridLayoutGroup>();
            if (!isFixed) number = (int)Math.Round(_width / (_cellSize.x + grid.spacing.x));
            number = number == 0 ? 1 : number;
            if (minNumber > 0 && number < minNumber)
                number = minNumber;
            else if (maxNumber > 0 && number > maxNumber)
                number = maxNumber;
            var c = Mathf.Ceil(total / (float)number);

            if (isFill)
                grid.cellSize =
                    new Vector2(
                        (_width - ((number - 1) * grid.spacing.x) - grid.padding.left - grid.padding.right) / number,
                        grid.cellSize.y);
            if (debug) Debug.LogErrorFormat("GridCompute size: {0} {1} {2}", grid.cellSize, _width, _width - ((number - 1) * grid.spacing.x) - grid.padding.left - grid.padding.right);
            if (vertical)
            {
                var height = c * grid.cellSize.y;
                height += (c - 1) * grid.spacing.y;
                height += grid.padding.top;
                height += grid.padding.bottom;
                SetHeight(height);
            }

            // 未测试
            if (horizontal)
            {
                var width = number * grid.cellSize.x;
                width += (number - 1) * grid.spacing.x;
                width += grid.padding.left;
                width += grid.padding.right;
                SetWidth(width);
            }
        }

        protected void Compute()
        {
            if (isFull)
            {
                var scale = list.Count > number ? 1 / (float)list.Count / (1 / (float)number) : 1;
                for (var i = 0; i < list.Count; i++)
                    list[i].gameObject.transform.localScale = new Vector3(scale, scale, scale);
            }
            else
            {
                if (vertical)
                {
                    if (total == 0)
                    {
                        SetHeight(0);
                    }
                    else
                    {
                        var group = content.GetComponent<VerticalLayoutGroup>();
                        var height = GetHeight(total);
                        height += (total - 1) * group.spacing;
                        height += group.padding.top;
                        height += group.padding.bottom;
                        if (isSelect && selectIndex >= 0) height += selectSize.y;
                        SetHeight(height);
                    }
                }

                if (horizontal)
                {
                    if (total == 0)
                    {
                        SetWidth(0);
                    }
                    else
                    {
                        var group = content.GetComponent<HorizontalLayoutGroup>();
                        var width = GetWidth(total);
                        width += (total - 1) * group.spacing;
                        width += group.padding.left;
                        width += group.padding.right;
                        if (isSelect && selectIndex >= 0) width += selectSize.x;
                        SetWidth(width);
                    }
                }
            }
        }

        private float GetHeight(int num)
        {
            if (isChange)
            {
                float h = 0;
                for (var i = 0; i < num; i++) h += list[i].GetComponent<RectExtend>().GetHeight();

                return h;
            }

            return num * item.GetComponent<RectTransform>().rect.height;
        }

        private float GetWidth(int num)
        {
            if (isChange)
            {
                float h = 0;
                for (var i = 0; i < num; i++) h += list[i].GetComponent<RectExtend>().GetWidth();

                return h;
            }

            return num * item.GetComponent<RectTransform>().rect.width;
        }

        private void SetHeight(float height)
        {
            if (isCenter)
            {
                rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
                parentRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,
                    height < _height ? height : _height);
            }
            else
            {
                // _currentHeight = height > _height ? height : _height;
                rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
            }
        }

        private void SetWidth(float width)
        {
            if (isCenter)
            {
                rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
                parentRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,
                    width < _width ? width : _width);
            }
            else
            {
                // _currentWidth = width > _width ? width : _width;
                rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
            }
        }

        protected void ToIndex(int index)
        {
            if (vertical)
            {
                var group = content.GetComponent<VerticalLayoutGroup>();
                var height = index * item.GetComponent<RectTransform>().rect.height;
                height += (index - 1) * group.spacing;
                height += group.padding.top;
                height += group.padding.bottom;
                rectTransform.anchoredPosition = new Vector2(0, height + _currentHeight / 2);
            }

            if (horizontal)
            {
                var group = content.GetComponent<HorizontalLayoutGroup>();
                var width = index * item.GetComponent<RectTransform>().rect.width;
                width += (index - 1) * group.spacing;
                width += group.padding.left;
                width += group.padding.right;
                rectTransform.anchoredPosition = new Vector2(width + _currentWidth / 2, 0);
            }
        }

        protected void ItemHandle(int i, GameObject obj, JSONObject json)
        {
            if (isSelect)
            {
                var helper = obj.GetComponent<ListItemHelper>();
                BindEvent(helper);
                helper.SetIndex(isKey ? index : i);
                helper.SetData(allData ? GetAllData() : json);
                helper.SetSelect(i == selectIndex);
            }
            else
            {
                // Debug.LogErrorFormat("ItemHandle: {0} {1} {2}", i , obj, json);
                var helper = obj.GetComponent<NodeHelper>();
                BindEvent(helper);
                helper.SetIndex(isKey ? index : i);
                helper.SetData(allData ? GetAllData() : json);
            }
        }

        public void RefreshWidthHeight(float width, float height)
        {
            if (debug) Debug.LogErrorFormat("RefreshWidthHeight: {0} {1}", width, height);
            if (isGrid)
                if (_width == 0)
                    _cellSize = content.GetComponent<GridLayoutGroup>().cellSize;
            _width = width;
            _height = height;
            // Debug.LogErrorFormat("RefreshWidthHeight: {0}", content.activeSelf);
            if (content.activeSelf) Refresh();
        }

        [Button]
        public void Refresh()
        {
            if (isGrid) GridCompute();

            if (isAuto) Compute();

            if (layoutGroup != null)
            {
                if (vertical) layoutGroup.SetLayoutVertical();
                if (horizontal) layoutGroup.SetLayoutHorizontal();
            }
            
            if (adaptiveSize != null) adaptiveSize.Refresh();
        }
    }
}